package com.rightrule.service.impl;


import com.alibaba.druid.util.StringUtils;
import com.rightrule.rule.component.LoadPackageSnapshot;
import com.rightrule.rule.constans.StrategyDivisionTypeEnum;
import com.rightrule.rule.dto.DecisionStrategyDivisionConfigDTO;
import com.rightrule.rule.dto.RouterReqDTO;
import com.rightrule.rule.dto.RouterResultDTO;
import com.rightrule.rule.dto.StrategyDivisionSnapshotDTO;
import com.rightrule.rule.executor.RuleFlow;
import com.rightrule.service.StrategyRouterService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;

@Service
@Slf4j
public class StrategyRouterServiceImpl implements StrategyRouterService {


    @Autowired
    private LoadPackageSnapshot loadPackageSnapshot;

    private static final Integer DIVISION_RANG_LEN = 2;


    /**
     * 业务决策入口路由接口，判断具体走哪个策略版本
     *
     * @param routerReqDTO
     * @return
     */
    @Override
    public RouterResultDTO router(RouterReqDTO routerReqDTO) {
        log.info("router req={}", routerReqDTO);

        RouterResultDTO routerResultDTO = new RouterResultDTO();

        // 将事件变量带入决策变量中
        routerReqDTO.getSessionData().put("ipId", routerReqDTO.getIpId());
        routerReqDTO.getSessionData().put("businessId", routerReqDTO.getBusinessId());

        // 获取当前策略码在线的流量配置
        StrategyDivisionSnapshotDTO divisionConfig = loadPackageSnapshot.getOnlineDivisionCacheLogic().get(routerReqDTO.getStrategyCode());
        // todo 如果为空则去远程再次拉取
        if (Objects.isNull(divisionConfig)) {

        }

        // 如有策略流量配置还是为空则抛异常
        if (Objects.isNull(divisionConfig)) {
            log.error("router config is null={}", routerReqDTO);
            throw new RuntimeException("router config is null");
        }

        // 如果是不分流，则默认取第一个版本
        if (StrategyDivisionTypeEnum.NO_DIVISION.getCode().equals(divisionConfig.getDivisionType())) {
            fillRouterResult(routerResultDTO, divisionConfig.getDivisionConfigs().get(0));
        }

        // 按照执行key进行分流
        else if (StrategyDivisionTypeEnum.KEY_DIVISION.getCode().equals(divisionConfig.getDivisionType())) {
            for (DecisionStrategyDivisionConfigDTO versionConfig : divisionConfig.getDivisionConfigs()) {
                // 取出当前指定的分流key
                String divisionFieldValueStr = routerReqDTO.getSessionData().get(versionConfig.getDivisionField()).toString();
                // 通过当前业务标识，匹配对应的执行版本
                if (isMatchDivisionConfig(divisionFieldValueStr, versionConfig)) {
                    // 路由匹配成功，设置匹配版本，依赖的输入变量
                    fillRouterResult(routerResultDTO, versionConfig);
                    break;
                }
            }
        }

        // 如果是随机分配一定比例的流量
        else if (StrategyDivisionTypeEnum.RANDOM_DIVISION.getCode().equals(divisionConfig.getDivisionType())) {
            Random random = new Random();
            Integer randomNum = random.nextInt(100);
            for (DecisionStrategyDivisionConfigDTO versionConfig : divisionConfig.getDivisionConfigs()) {
                Integer start = versionConfig.getDivisionRange().get(0);
                Integer end = versionConfig.getDivisionRange().get(1);
                // 如果本地随机数在当前范围内则设置匹配版本，依赖的输入变量
                if (randomNum >= start && randomNum <= end) {
                    fillRouterResult(routerResultDTO, versionConfig);
                    break;
                }
            }
        }

        // 如果路由不到对应的版本则抛异常,todo 后续引入监控，需要监控埋点
        if (routerResultDTO.getStrategyVersionId() == null) {
            log.error("router is empty req={}", routerReqDTO);
            throw new RuntimeException("router empty");
        }

        log.info("router response ={}", routerResultDTO);
        return routerResultDTO;
    }

    /**
     * 设置对应的版本变量
     *
     * @param routerResultDTO
     * @param strategyDivisionConfigDTO
     */
    private void fillRouterResult(RouterResultDTO routerResultDTO, DecisionStrategyDivisionConfigDTO strategyDivisionConfigDTO) {
        Long strategyVersionId = strategyDivisionConfigDTO.getStrategyVersionId();
        routerResultDTO.setStrategyVersionId(strategyVersionId);

        // 获取版本对应的输入变量,和事件变量
        Map<String, RuleFlow> onlineSnapshotCacheLogic = loadPackageSnapshot.getOnlineSnapshotCacheLogic();

        RuleFlow ruleFlow = onlineSnapshotCacheLogic.get(strategyDivisionConfigDTO.getStrategyCode() + "_" + strategyVersionId);
        if (Objects.isNull(ruleFlow)) {
            throw new RuntimeException("router fail ruleFlow is null");
        }

        routerResultDTO.setEventFields(ruleFlow.getEventField());
        routerResultDTO.setInputFields(ruleFlow.getInputField());

    }


    /**
     * 判断是否通过业务主键匹配对应的版本
     *
     * @param divisionFieldValueStr
     * @param versionDivisionConfig
     * @return
     */
    private boolean isMatchDivisionConfig(String divisionFieldValueStr, DecisionStrategyDivisionConfigDTO versionDivisionConfig) {
        List<Integer> divisionRange = versionDivisionConfig.getDivisionRange();
        List<Integer> divisionIndex = versionDivisionConfig.getFieldIndex();

        // 检测配置是否正确
        if (!CollectionUtils.isEmpty(divisionRange) && DIVISION_RANG_LEN.equals(divisionRange.size())
                && DIVISION_RANG_LEN.equals(divisionIndex.size())
                && divisionFieldValueStr.length() > divisionIndex.get(DIVISION_RANG_LEN - 1)
        ) {

            Integer firstRang = versionDivisionConfig.getDivisionRange().get(0);
            Integer secondRang = versionDivisionConfig.getDivisionRange().get(1);

            Integer firstIndex = versionDivisionConfig.getFieldIndex().get(0);
            Integer secondIndex = versionDivisionConfig.getFieldIndex().get(1);

            // 取出key对应的的分流下标
            String fieldValueStr = divisionFieldValueStr.substring(firstIndex, firstIndex + 1) + divisionFieldValueStr.substring(secondIndex, secondIndex + 1);
            // 如果是纯数字则直接判断
            if (StringUtils.isNumber(fieldValueStr)) {
                Integer divisionValueInt = Integer.parseInt(fieldValueStr);
                if (divisionValueInt > firstRang && divisionValueInt <= secondRang) {
                    return true;
                }
            }
            // 如果是字符串
            else {
                String hexFieldValue = DigestUtils.md5Hex(fieldValueStr);
                int hexFieldValueInt = Integer.parseInt(hexFieldValue.substring(hexFieldValue.length() - 2, hexFieldValue.length()), 16);
                Integer divisionValueInt = hexFieldValueInt % 100;
                if (divisionValueInt >= firstRang && divisionValueInt <= secondRang) {
                    return true;
                }
            }
        }
        return false;
    }
}
